import 'dart:convert';
import 'dart:io';

import 'package:base_moudle/config/zs_network_config.dart';
import 'package:base_moudle/uitils/hy_log_util.dart';
import 'package:base_moudle/uitils/zs_memery_cache.dart';
import 'package:dio/dio.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter_easyloading/flutter_easyloading.dart';
// import 'package:fluttertoast/fluttertoast.dart';
import 'package:get/get.dart' as GETX;

const List<String> methodList = ["post", 'get', 'put', 'delete', 'head'];

enum ParamType { nomarl, formData, raw }

enum NetworkcontentType { json, xwwwformurlencoded, formData}

class NetworkManager {
  // static _instance，_instance会在编译期被初始化，保证了只被创建一次
  static final NetworkManager _instance = NetworkManager._internal();

  //提供了一个工厂方法来获取该类的实例
  factory NetworkManager() {
    return _instance;
  }
  bool enableLog = true;
  // 通过私有方法_internal()隐藏了构造方法，防止被误创建
  NetworkManager._internal() {
    // 初始化
    init();
  }

  setContentType(contentType) {
    BaseOptions _options = BaseOptions(
        baseUrl: zs_host(),
        connectTimeout: Duration(milliseconds: 50000),
        receiveTimeout: Duration(milliseconds: 50000),
        contentType: contentType);
    _dio?.options = _options;
  }

  init() {
    BaseOptions _options = BaseOptions(
        baseUrl: zs_host(),
        connectTimeout: Duration(milliseconds: 50000),
        receiveTimeout: Duration(milliseconds: 50000),
        maxRedirects: 3,
        contentType: 'application/json');
    _dio = Dio(_options);
    // if (Platform.isIOS || Platform.isAndroid) {
    //   (_dio!.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate =
    //       (client) {
    //     client.badCertificateCallback = (cert, host, port) {
    //       return true;
    //     };
    //   };
    // }
  }

  void resetBaseUrl() {
    _dio?.options.baseUrl = zs_host();
  }

  Dio? _dio;

  // post请求
  Future<ResultData?> post({
    @required String? path,
    bool needToken = true,
    ParamType? paramType,
    NetworkcontentType? contentType,
    String? otherHost,
    dynamic dynamicParams,
    Map<String, dynamic>? queryParams,
    Map<String, dynamic>? params,
    Map<String, dynamic>? headerExtr,
    bool isShowAlert = true,
    bool showProgress = true,
  }) async {
    return await _request(
        path: path,
        otherHost: otherHost,
        queryParams: queryParams,
        paramType: paramType,
        dynamicParams: dynamicParams,
        method: 'post',
        params: params,
        isShowAlert: isShowAlert,
        showProgress: showProgress,
        headerExtr: headerExtr,
        contentType: contentType,
        needToken: needToken);
  }

  // get请求
  Future<ResultData?> get({
    @required String? path,
    String? otherHost,
    bool needToken = true,
    ParamType? paramType,
    dynamic dynamicParams,
    Map<String, dynamic>? queryParams,
    Map<String, dynamic>? params,
    Map<String, dynamic>? headerExtr,
    NetworkcontentType? contentType,
    bool isShowAlert = true,
    bool showProgress = true,
  }) async {
    return await _request(
        path: path,
        otherHost: otherHost,
        method: 'get',
        queryParams: queryParams,
        dynamicParams: dynamicParams,
        paramType: paramType,
        params: params,
        isShowAlert: isShowAlert,
        showProgress: showProgress,
        headerExtr: headerExtr,
        contentType: contentType,
        needToken: needToken);
  }

  Future<String> downloadFile(
      {required downloadUrl,
      required String savePath,
      required String fileName}) async {
    Response? response;
    var dio = Dio();

    try {
      response = await dio.download(downloadUrl, '$savePath/$fileName');
      print(response);
    } catch (e) {
      return Future.value('400');
    }
    if (response?.statusCode == 200) {
      return Future.value('$savePath/$fileName');
    } else {
      return Future.value('400');
    }
  }

  Future<ResultData?> _request({
    @required String? path,
    @required String? method,
    bool needToken = true,
    bool showProgress = true,
    ParamType? paramType,
    String? otherHost,
    Map<String, dynamic>? headerExtr,
    Map<String, dynamic>? queryParams,
    dynamic dynamicParams,
    Map<String, dynamic>? params,
    NetworkcontentType? contentType,
    bool isShowAlert = true,
  }) async {
    LOGGERINFO('------进入请求');
    if (contentType == NetworkcontentType.xwwwformurlencoded) {
      if (_dio!.options.contentType != 'x-www-form-urlencoded')
        setContentType('application/x-www-form-urlencoded');
    } else if (contentType == NetworkcontentType.formData) {
      if (_dio!.options.contentType != 'multipart/form-data')
        setContentType('multipart/form-data');
    } else {
      if (_dio!.options.contentType != 'application/json')
        setContentType('application/json');
    }
    if (needToken) {
      /// 需要token，插入token
      if (ZsMemeryCache.loginDataModel == null) {
        LOGGERINFO('------进入请求--token==null');
        // GetX.Get.toNamed(ZsRouter.zsLoginRouter);
        ///没有token，应该跳转到登录页面进行登录操作
        _noAuthorizationLogic();
        return Future.value(ResultData.fromJson({"data": {}}, 401));
      } else {
        /// Map 的存取及更新
        ///putifAbsent向一个 Map 中添加不存在的键值对，如果 key 已经存在，则原 Map 不变。
        ///update提供一个 key, 更新 Map 中跟 key 对应的 value。
        ///containsKey() 判断一个 Map 是否包含某个 key
        ///containsValue() 判断一个 Map 是否包含某个 value
        if (_dio!.options.headers.containsKey('Authorization')) {
          _dio!.options.headers.update(
              'Authorization',
              (value) =>
                  'Bearer ${ZsMemeryCache.loginDataModel!.tokens!.accessToken}');
        } else {
          _dio!.options.headers.putIfAbsent(
              'Authorization',
              () =>
                  'Bearer ${ZsMemeryCache.loginDataModel!.tokens!.accessToken}');
        }
      }
      LOGGERINFO('------->>>>>>>${_dio!.options.headers}');
    } else {
      _dio!.options.headers.remove('Authorization');
    }
    _dio!.options.headers
        .putIfAbsent('U_A', () => '${Platform.isAndroid ? "Android" : 'iOS'}');
    _dio!.options.headers
        .putIfAbsent('platform', () => 'App/${Platform.isAndroid ? "Android" : 'iOS'}');
    if (headerExtr != null) {
      _dio!.options.headers.addAll(headerExtr);
    }
    if (method != null && methodList.contains(method)) {
      _dio!.options.method = method;
    }
    if (otherHost != null) {
      _dio!.options.baseUrl = otherHost;
    } else {
      _dio!.options.baseUrl = baseHostUrl;
    }
    LOGGERINFO('-----------请求开始了--${method}-url = $path--------');
    var data;
    if (paramType == ParamType.nomarl) {
      data = params;
    } else if (paramType == ParamType.formData) {
      data = params != null ? FormData.fromMap(params) : params;
    } else if (paramType == ParamType.raw) {
      data = json.encode(params);
    }

    Response? response;
    ResultData? resultData;
    var logBaseUrl = _dio?.options.baseUrl.replaceAll('http', '');
    try {
      LOGGERINFO('-------network-url = $logBaseUrl$path');
      LOGGERINFO(
          '-------接口调用返回前LOG-\n{\n\t\t【域名】$logBaseUrl\n\t\t【接口名称】$path\n\t\t【请求方法】$method\n\t\t【请求参数】${JsonEncoder().convert(data != null ? params : params != null ?  params: queryParams)}\n\t\t【请求头信息】${_dio?.options?.headers}\n}');
      if (paramType == ParamType.formData) {
        response = await _dio!.request(path!,
            data: data,
            options: Options(
                method: method,
                contentType: 'multipart/form-data',
                responseType: ResponseType.plain));
      } else {
        response = await _dio!.request(
          path!,
          queryParameters: queryParams,
          data: dynamicParams != null ? dynamicParams : data,
        );
      }
      LOGGERINFO(
          '-------接口调用返回LOG-\n{\n\t\t【域名】$logBaseUrl\n\t\t【接口名称】$path\n\t\t【请求方法】$method\n\t\t【请求参数】${JsonEncoder().convert(data != null ? params : params != null ? params : queryParams)}\n\t\t【请求头信息】${_dio?.options?.headers}\n\t\t【返回参数】${json.encode(response.data)}\n}');

      if (response.statusCode == 200) {
        if (response.data.runtimeType == Map &&
            response.data['success'] != null) {
          resultData = ResultData.fromJsonNew(response.data);
        } else if (response.data.runtimeType == Map &&
            response.data['code'] == '200') {
          resultData = ResultData.fromJsonNew(
              {'success': true, 'code': 0, 'data': response.data});
        } else {
          if (response.data.runtimeType == String) {
            if (path.startsWith('/vmc/api/ptz')) {
              resultData = ResultData.fromJsonNew(
                  {'success': true, 'code': 0, 'data': response.data});
            } else {
              if (path.startsWith('/vmc/api/playback/start')) {
                resultData = ResultData.fromJsonNew(
                    {'success': true, 'code': 0, 'data': response.data});
              } else {
                resultData = ResultData.fromJson(
                    jsonDecode(response.data), response.statusCode);
              }
            }
          } else {
            resultData =
                ResultData.fromJson(response.data, response.statusCode);
          }
        }
      } else if (response.statusCode == 401 || resultData?.code == 401) {
        _noAuthorizationLogic();
      }
      if (showProgress) {
        EasyLoading.dismiss();
      }
    } catch (e) {
      EasyLoading.dismiss();
      LOGGERERR(
          '-------接口调用返回LOG-\n{\n\t\t【域名】$logBaseUrl\n\t\t【接口名称】$path\n\t\t【请求方法】$method\n\t\t【请求参数】${JsonEncoder().convert(data != null ? params : params != null ? queryParams : dynamicParams)}\n\t\t【请求头信息】${_dio?.options?.headers}\n\t\t【network-error】[${e.toString()}]\n}');

      if (e.runtimeType == DioError) {
        var err = e as DioError;
        if (err.type == DioExceptionType.receiveTimeout ||
            err.type == DioExceptionType.sendTimeout ||
            err.type == DioExceptionType.connectionTimeout) {
          EasyLoading.showToast('网络连接超时，请重试或联系管理员');
        } else if (err.type == DioExceptionType.badResponse) {
          LOGGERINFO('-------network-error[${e.toString()}]--------');
          if ((path?.startsWith('/vmc/api') ?? false) || !isShowAlert) {
            return Future.value(ResultData.fromJsonNew({
              'success': false,
              'code': e.response?.statusCode ?? 0,
              'data': ''
            }));
          } else {
            EasyLoading.showToast('服务器错误');
          }
        }
      }
      // DioErrorType.receiveTimeout
      if (e.toString().contains("401")) {
        /// token过期了，应该使用refresh—token进行换取新的token
        _noAuthorizationLogic();
        EasyLoading.showToast('帐号验证过期，请重新登录');
      } else if (e.toString().contains("500")) {
        return Future.value();
      } else {
        if (((e as DioError).error as SocketException?)?.osError?.errorCode ==
            7) {
          EasyLoading.showToast('网络错误，请检查网络并重试···');
        } else {
          EasyLoading.showToast('出错了:${e.hashCode}');
        }
        return Future.value(null);
      }
    }
    if (resultData == null) {
      return Future.value(null);
    }
    if (resultData.code == 401) {}
    if (resultData.code != null) {
      try {
        if (!resultData.isSuccess) {
          if ((path?.startsWith('/vmc/api') ?? false) || !isShowAlert) {
          } else {
            if (resultData.message != null) {
              EasyLoading.showToast(resultData.message.toString());
            }
          }
        } else if (resultData.code?.toInt() == 3001) {
          // EasyLoading.showSuccess(resultData.message ?? '操作成功');
        } else if (resultData.code?.toInt() == 2001) {
          // EasyLoading.showSuccess(resultData.message ?? '操作成功');
        } else if (resultData.code?.toInt() != 1 &&
            resultData.code?.toInt() != 4001 &&
            resultData.code?.toInt() != 1001) {
          if (resultData.message != '查询失败' && resultData.message != '失败') {
            // if (isShowAlert) EasyLoading.showError(resultData.message ?? '操作失败');
          }

          // Fluttertoast.showToast(msg: resultData.message.toString());
        }
      } catch (e) {
        LOGGERERR('-------network-解析错误吗失败--------');
      }
    }
    return Future.value(resultData);
  }

  LOGGERINFO(text) {
    if (enableLog) {
      HYLogUtil.info(text);
    }
  }

  LOGGERERR(text) {
    if (enableLog) {
      HYLogUtil.error(text);
    }
  }
}

_noAuthorizationLogic() async {
  await ZsMemeryCache.removeUserInfo();
  GETX.Get.toNamed('/future/phoneLogin');
}

class ResultData {
  int? code;
  int? resultCode;
  int? total;
  int? pages;
  bool? success;
  var data;
  String? errMsg;
  String? message;
  var responseData;

  bool get _isOK => [1, 1001, 2001, 3001, 4001].contains(code);
  bool get isSuccess => success ?? _isOK;

  bool dataIsEmpty() {
    if (data == null) {
      return false;
    }
    if (this.data.runtimeType == 'List' ||
        this.data.runtimeType == 'Iterable') {
      return data.length() == 0;
    }
    return true;
  }

  ResultData.fromJson(var result, int? statusCode) {
    this.responseData = result;
    if (statusCode == 200) {
      if (result is List) {
        this.data = result;
      } else {
        if (result.containsKey('code')) {
          if (result['code'] is String) {
            this.code = int.parse(result['code'] as String);
          } else {
            this.code = result['code'] as int? ?? 0;
          }

          this.errMsg = result['errMsg'];
        } else {
          this.code = result['result'];
          this.resultCode = this.code;
        }
        if (this.code == null) {
          this.data = result;
        } else {
          this.data = result['data'];
          this.message = result['msg'];
          this.errMsg = result['errMsg'];
        }
      }
      print(this.data);

      /// 请求成功
    } else if (statusCode == 401) {
      ///未登陆，去登陆
      this.code = result['code'];
      this.data = result['data'];
      this.message = result['msg'];
      _noAuthorizationLogic();
    } else if (statusCode == 500) {
      /// 服务器错误

    } else {
      /// 位置错误
    }
  }

  ResultData.fromJsonNew(var result) {
    this.responseData = result;
    this.success = result['success'];
    if (result['success']) {
      this.code = result['code'];
      this.data = result['data'];
      this.resultCode = this.code;
      this.message = result['msg'];
      this.total = result['total'] as int?;
      this.pages = result['pages'] as int?;
      print(this.data);

      /// 请求成功
    } else {
      this.code = result['code'];
      this.data = result['data'];
      this.resultCode = this.code;
      this.errMsg = result['msg'];
    }
  }
}

abstract class NetworkMixin {
  Future<ResultData?> post({
    @required String? path,
    String? otherHost,
    ParamType paramType = ParamType.raw,
    bool needToken = true,
    bool showProgress = true,
    dynamic dynamicParams,
    Map<String, dynamic>? queryParams,
    Map<String, dynamic>? params,
    Map<String, dynamic>? headerExtr,
    NetworkcontentType? contentType = NetworkcontentType.json,
    bool isShowAlert = true,
  }) async {
    // print('===>>>> 进入mixin');
    return NetworkManager().post(
      path: path,
      otherHost: otherHost,
      paramType: paramType,
      needToken: needToken,
      showProgress: showProgress,
      queryParams: queryParams,
      dynamicParams: dynamicParams,
      params: params,
      headerExtr: headerExtr,
      contentType: contentType,
      isShowAlert: isShowAlert,
    );
  }

  Future<ResultData?> get({
    @required String? path,
    String? otherHost,
    ParamType? paramType = ParamType.nomarl,
    bool needToken = true,
    dynamic dynamicParams,
    Map<String, dynamic>? queryParams,
    Map<String, dynamic>? params,
    Map<String, dynamic>? headerExtr,
    NetworkcontentType? contentType = NetworkcontentType.json,
    bool isShowAlert = true,
  }) async {
    return await NetworkManager().get(
      path: path,
      otherHost: otherHost,
      paramType: paramType,
      needToken: needToken,
      queryParams: queryParams,
      dynamicParams: dynamicParams,
      params: params,
      headerExtr: headerExtr,
      contentType: contentType,
      isShowAlert: isShowAlert,
    );
  }

  Future<String> downloadFile(
      {required downloadUrl,
      required String savePath,
      required String fileName}) async {
    return NetworkManager().downloadFile(
        downloadUrl: downloadUrl, savePath: savePath, fileName: fileName);
  }
}
